use crate::{
    config::{get_param, Param},
    route::get_file,
};
use axum::{
    extract::{DefaultBodyLimit, Request, State},
    middleware::{from_fn_with_state, Next},
    response::Response,
    routing::{get, post},
    Router,
};
use data::AppState;
use fast_qr::QRBuilder;
use route::upload;

use tokio::signal;
use utils::{get_data, print_banner};

mod config;
mod data;
mod route;
mod utils;

#[tokio::main]
async fn main() {
    let Param { ip, port, .. } = get_param();
    let data = get_data();
    print_banner();
    let app = Router::new()
        .route("/", get(get_file))
        .route("/upload", post(upload).layer(DefaultBodyLimit::disable()))
        .layer(from_fn_with_state(data.clone(), request_count_limit))
        .with_state(data);
    let address = format!("{}:{}", ip, port);
    let url = format!("http://{}", address);
    gen_qrcode(&url);
    let listener = tokio::net::TcpListener::bind(address).await.unwrap();
    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal())
        .await
        .unwrap();
}

fn gen_qrcode<T: AsRef<str>>(url: T) {
    let url = url.as_ref().to_string();
    let code = QRBuilder::new(url).build().unwrap();
    let str = code.to_str();
    println!("{}", str);
}

async fn shutdown_signal() {
    let ctrl_c = async {
        signal::ctrl_c()
            .await
            .expect("failed to install Ctrl+C handler");
    };
    #[cfg(unix)]
    let terminate = async {
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("failed to install signal handler")
            .recv()
            .await
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {},
        _ = terminate => {}
    }
}

async fn request_count_limit(
    State(state): State<AppState>,
    request: Request,
    next: Next,
) -> Response {
    let response = next.run(request).await;
    let mut count = state.count.lock().expect("mutex was poisoned");
    println!("count is {:?}", count);
    if *count <= 1 {
        nix::sys::signal::raise(nix::sys::signal::SIGINT).expect("cannot send ctrl-c");
    } else {
        *count -= 1;
    }
    response
}
